/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import org.eclipse.core.commands.common.EventManager; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.dynamichelpers.ExtensionTracker; import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler; import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IElementFactory; import org.eclipse.ui.IMemento; import org.eclipse.ui.IPersistableElement; import org.eclipse.ui.IWorkbenchPreferenceConstants; import org.eclipse.ui.IWorkingSet; import org.eclipse.ui.IWorkingSetElementAdapter; import org.eclipse.ui.IWorkingSetManager; import org.eclipse.ui.IWorkingSetUpdater; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.XMLMemento; import org.eclipse.ui.dialogs.IWorkingSetEditWizard; import org.eclipse.ui.dialogs.IWorkingSetNewWizard; import org.eclipse.ui.dialogs.IWorkingSetPage; import org.eclipse.ui.dialogs.IWorkingSetSelectionDialog; import org.eclipse.ui.internal.dialogs.WorkingSetEditWizard; import org.eclipse.ui.internal.dialogs.WorkingSetNewWizard; import org.eclipse.ui.internal.dialogs.WorkingSetSelectionDialog; import org.eclipse.ui.internal.misc.StatusUtil; import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants; import org.eclipse.ui.internal.registry.WorkingSetDescriptor; import org.eclipse.ui.internal.registry.WorkingSetRegistry; import org.eclipse.ui.internal.util.PrefUtil; import org.eclipse.ui.progress.WorkbenchJob; import org.eclipse.ui.statushandlers.StatusManager; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.BundleListener; /** * Abstract implementation of <code>IWorkingSetManager</code>. */ public abstract class AbstractWorkingSetManager extends EventManager implements IWorkingSetManager, BundleListener, IExtensionChangeHandler { static abstract class WorkingSetRunnable implements ISafeRunnable { @Override public void handleException(Throwable exception) { StatusManager.getManager().handle( StatusUtil.newStatus(PlatformUI.PLUGIN_ID, exception)); } } private SortedSet<AbstractWorkingSet> workingSets = new TreeSet<>((o1, o2) -> o1.getUniqueId().compareTo(o2.getUniqueId())); private List<IWorkingSet> recentWorkingSets = new ArrayList<>(); private BundleContext bundleContext; private Map<String, IWorkingSetUpdater> updaters = new HashMap<>(); private Map<String, IWorkingSetElementAdapter> elementAdapters = new HashMap<>(); private static final IWorkingSetUpdater NULL_UPDATER = new IWorkingSetUpdater() { @Override public void add(IWorkingSet workingSet) { } @Override public boolean remove(IWorkingSet workingSet) { return true; } @Override public boolean contains(IWorkingSet workingSet) { return true; } @Override public void dispose() { } }; private static final IWorkingSetElementAdapter IDENTITY_ADAPTER = new IWorkingSetElementAdapter() { @Override public IAdaptable[] adaptElements(IWorkingSet ws, IAdaptable[] elements) { return elements; } @Override public void dispose() { } }; /** * Returns the descriptors for the given editable working set ids. If an id * refers to a missing descriptor, or one that is non-editable, it is * skipped. If <code>null</code> is passed, all editable descriptors are * returned. * * @param supportedWorkingSetIds * the ids for the working set descriptors, or <code>null</code> * for all editable descriptors * @return the descriptors corresponding to the given editable working set * ids */ private static WorkingSetDescriptor[] getSupportedEditableDescriptors(String[] supportedWorkingSetIds) { WorkingSetRegistry registry = WorkbenchPlugin.getDefault().getWorkingSetRegistry(); if (supportedWorkingSetIds == null) { return registry.getNewPageWorkingSetDescriptors(); } List<WorkingSetDescriptor> result = new ArrayList<>(supportedWorkingSetIds.length); for (String supportedWorkingSetId : supportedWorkingSetIds) { WorkingSetDescriptor desc = registry.getWorkingSetDescriptor(supportedWorkingSetId); if (desc != null && desc.isEditable()) { result.add(desc); } } return result.toArray(new WorkingSetDescriptor[result.size()]); } protected AbstractWorkingSetManager(BundleContext context) { bundleContext= context; bundleContext.addBundleListener(this); PlatformUI.getWorkbench().getExtensionTracker().registerHandler(this, ExtensionTracker .createExtensionPointFilter(getExtensionPointFilter())); } /** * Returns the working sets extension point. * * @return the working sets extension point * @since 3.3 */ private IExtensionPoint getExtensionPointFilter() { return Platform.getExtensionRegistry().getExtensionPoint( PlatformUI.PLUGIN_ID, IWorkbenchRegistryConstants.PL_WORKINGSETS); } @Override public void dispose() { bundleContext.removeBundleListener(this); for (IWorkingSetUpdater next : updaters.values()) { SafeRunner.run(new WorkingSetRunnable() { @Override public void run() throws Exception { next.dispose(); } }); } for (IWorkingSetElementAdapter next : elementAdapters.values()) { SafeRunner.run(new WorkingSetRunnable() { @Override public void run() throws Exception { next.dispose(); } }); } } //---- working set creation ----------------------------------------------------- @Override public IWorkingSet createWorkingSet(String name, IAdaptable[] elements) { return new WorkingSet(name, name, elements); } @Override public IWorkingSet createAggregateWorkingSet(String name, String label, IWorkingSet[] components) { return new AggregateWorkingSet(name, label, components); } @Override public IWorkingSet createWorkingSet(IMemento memento) { return restoreWorkingSet(memento); } //---- working set management --------------------------------------------------- @Override public void addWorkingSet(IWorkingSet workingSet) { IWorkingSet wSet = getWorkingSet(workingSet.getName()); Assert.isTrue(wSet==null,"working set with same name already registered"); //$NON-NLS-1$ internalAddWorkingSet(workingSet); } private void internalAddWorkingSet(IWorkingSet workingSet) { AbstractWorkingSet abstractWorkingSet = (AbstractWorkingSet) workingSet; workingSets.add(abstractWorkingSet); abstractWorkingSet.connect(this); addToUpdater(workingSet); firePropertyChange(CHANGE_WORKING_SET_ADD, null, workingSet); } protected boolean internalRemoveWorkingSet(IWorkingSet workingSet) { boolean workingSetRemoved = workingSets.remove(workingSet); boolean recentWorkingSetRemoved = recentWorkingSets.remove(workingSet); if (workingSetRemoved) { ((AbstractWorkingSet)workingSet).disconnect(); removeFromUpdater(workingSet); firePropertyChange(CHANGE_WORKING_SET_REMOVE, workingSet, null); } return workingSetRemoved || recentWorkingSetRemoved; } @Override public IWorkingSet[] getWorkingSets() { SortedSet<IWorkingSet> visibleSubset = new TreeSet<IWorkingSet>(WorkingSetComparator.getInstance()); for (IWorkingSet workingSet : workingSets) { if (workingSet.isVisible()) { visibleSubset.add(workingSet); } } return visibleSubset.toArray(new IWorkingSet[visibleSubset.size()]); } @Override public IWorkingSet[] getAllWorkingSets() { IWorkingSet[] sets = workingSets.toArray(new IWorkingSet[workingSets.size()]); Arrays.sort(sets, WorkingSetComparator.getInstance()); return sets; } @Override public IWorkingSet getWorkingSet(String name) { if (name == null || workingSets == null) { return null; } for (IWorkingSet workingSet : workingSets) { if (name.equals(workingSet.getName())) { return workingSet; } } return null; } // ---- recent working set management -------------------------------------- @Override public IWorkingSet[] getRecentWorkingSets() { return recentWorkingSets.toArray(new IWorkingSet[recentWorkingSets.size()]); } /** * Adds the specified working set to the list of recently used * working sets. * * @param workingSet working set to added to the list of recently * used working sets. */ protected void internalAddRecentWorkingSet(IWorkingSet workingSet) { if (!workingSet.isVisible()) { return; } recentWorkingSets.remove(workingSet); recentWorkingSets.add(0, workingSet); sizeRecentWorkingSets(); } //---- equals and hash code ----------------------------------------------- /** * Tests the receiver and the object for equality * * @param object object to compare the receiver to * @return true=the object equals the receiver, it has the same * working sets. false otherwise */ @Override public boolean equals(Object object) { if (this == object) { return true; } if (!getClass().getName().equals(object.getClass().getName())) { return false; } AbstractWorkingSetManager other= (AbstractWorkingSetManager)object; return other.workingSets.equals(workingSets); } /** * Returns the hash code. * * @return the hash code. */ @Override public int hashCode() { return workingSets.hashCode(); } //---- property listeners ------------------------------------------------- @Override public void addPropertyChangeListener(IPropertyChangeListener listener) { addListenerObject(listener); } @Override public void removePropertyChangeListener(IPropertyChangeListener listener) { removeListenerObject(listener); } /** * Notify property change listeners about a change to the list of * working sets. * * @param changeId one of * IWorkingSetManager#CHANGE_WORKING_SET_ADD * IWorkingSetManager#CHANGE_WORKING_SET_REMOVE * IWorkingSetManager#CHANGE_WORKING_SET_CONTENT_CHANGE * IWorkingSetManager#CHANGE_WORKING_SET_NAME_CHANGE * @param oldValue the removed working set or null if a working set * was added or changed. * @param newValue the new or changed working set or null if a working * set was removed. */ protected void firePropertyChange(String changeId, Object oldValue, Object newValue) { final Object[] listeners = getListeners(); if (listeners.length == 0) { return; } final PropertyChangeEvent event = new PropertyChangeEvent(this, changeId, oldValue, newValue); Runnable notifier = () -> { for (Object listener : listeners) { final IPropertyChangeListener propertyChangeListener = (IPropertyChangeListener) listener; ISafeRunnable safetyWrapper = new ISafeRunnable() { @Override public void run() throws Exception { propertyChangeListener.propertyChange(event); } @Override public void handleException(Throwable exception) { // logged by the runner } }; SafeRunner.run(safetyWrapper); } }; // Notifications are sent on the UI thread. if (Display.getCurrent() != null) { notifier.run(); } else { // Use an asyncExec to avoid deadlocks. Display.getDefault().asyncExec(notifier); } } /** * Fires a property change event for the changed working set. Should only be * called by org.eclipse.ui.internal.WorkingSet. * * @param changedWorkingSet * the working set that has changed * @param propertyChangeId * the changed property. one of * CHANGE_WORKING_SET_CONTENT_CHANGE, * CHANGE_WORKING_SET_LABEL_CHANGE, and * CHANGE_WORKING_SET_NAME_CHANGE * @param oldValue * the old value */ public void workingSetChanged(IWorkingSet changedWorkingSet, String propertyChangeId, Object oldValue) { firePropertyChange(propertyChangeId, oldValue, changedWorkingSet); } // ---- Persistence // ---------------------------------------------------------------- /** * Saves all persistable working sets in the persistence store. * * @param memento the persistence store * @see IPersistableElement */ public void saveWorkingSetState(IMemento memento) { Iterator<AbstractWorkingSet> iterator = workingSets.iterator(); // break the sets into aggregates and non aggregates. The aggregates should be saved after the non-aggregates // so that on restoration all necessary aggregate components can be found. ArrayList<IWorkingSet> standardSets = new ArrayList<>(); ArrayList<IWorkingSet> aggregateSets = new ArrayList<>(); while (iterator.hasNext()) { IWorkingSet set = iterator.next(); if (set instanceof AggregateWorkingSet) { aggregateSets.add(set); } else { standardSets.add(set); } } saveWorkingSetState(memento, standardSets); saveWorkingSetState(memento, aggregateSets); } /** * @param memento the memento to save to * @param list the working sets to save * @since 3.2 */ private void saveWorkingSetState(final IMemento memento, List list) { for (Iterator i = list.iterator(); i.hasNext();) { final IPersistableElement persistable = (IWorkingSet) i.next(); SafeRunner.run(new WorkingSetRunnable() { @Override public void run() throws Exception { // create a dummy node to write too - the write could fail so we // shouldn't soil the final memento until we're sure it succeeds. XMLMemento dummy = XMLMemento.createWriteRoot(IWorkbenchConstants.TAG_WORKING_SET); dummy.putString(IWorkbenchConstants.TAG_FACTORY_ID, persistable.getFactoryId()); persistable.saveState(dummy); // if the dummy was created successfully copy it to the real output IMemento workingSetMemento = memento .createChild(IWorkbenchConstants.TAG_WORKING_SET); workingSetMemento.putMemento(dummy); } }); } } /** * Recreates all working sets from the persistence store * and adds them to the receiver. * * @param memento the persistence store */ protected void restoreWorkingSetState(IMemento memento) { for (IMemento child : memento.getChildren(IWorkbenchConstants.TAG_WORKING_SET)) { AbstractWorkingSet workingSet = (AbstractWorkingSet) restoreWorkingSet(child); if (workingSet != null) { internalAddWorkingSet(workingSet); } } } /** * Recreates a working set from the persistence store. * * @param memento the persistence store * @return the working set created from the memento or null if * creation failed. */ protected IWorkingSet restoreWorkingSet(final IMemento memento) { String factoryID = memento .getString(IWorkbenchConstants.TAG_FACTORY_ID); if (factoryID == null) { // if the factory id was not set in the memento // then assume that the memento was created using // IMemento.saveState, and should be restored using WorkingSetFactory factoryID = AbstractWorkingSet.FACTORY_ID; } final IElementFactory factory = PlatformUI.getWorkbench().getElementFactory( factoryID); if (factory == null) { WorkbenchPlugin .log("Unable to restore working set - cannot instantiate factory: " + factoryID); //$NON-NLS-1$ return null; } final IAdaptable[] adaptable = new IAdaptable[1]; SafeRunner.run(new WorkingSetRunnable() { @Override public void run() throws Exception { adaptable[0] = factory.createElement(memento); } }); if (adaptable[0] == null) { WorkbenchPlugin .log("Unable to restore working set - cannot instantiate working set: " + factoryID); //$NON-NLS-1$ return null; } if ((adaptable[0] instanceof IWorkingSet) == false) { WorkbenchPlugin .log("Unable to restore working set - element is not an IWorkingSet: " + factoryID); //$NON-NLS-1$ return null; } return (IWorkingSet) adaptable[0]; } /** * Saves the list of most recently used working sets in the persistence * store. * * @param memento the persistence store */ protected void saveMruList(IMemento memento) { Iterator iterator = recentWorkingSets.iterator(); while (iterator.hasNext()) { IWorkingSet workingSet = (IWorkingSet) iterator.next(); IMemento mruMemento = memento .createChild(IWorkbenchConstants.TAG_MRU_LIST); mruMemento.putString(IWorkbenchConstants.TAG_NAME, workingSet .getName()); } } /** * Restores the list of most recently used working sets from the * persistence store. * * @param memento the persistence store */ protected void restoreMruList(IMemento memento) { IMemento[] mruWorkingSets = memento .getChildren(IWorkbenchConstants.TAG_MRU_LIST); for (int i = mruWorkingSets.length - 1; i >= 0; i--) { String workingSetName = mruWorkingSets[i] .getString(IWorkbenchConstants.TAG_NAME); if (workingSetName != null) { IWorkingSet workingSet = getWorkingSet(workingSetName); if (workingSet != null) { internalAddRecentWorkingSet(workingSet); } } } } //---- user interface support ----------------------------------------------------- /** * @see org.eclipse.ui.IWorkingSetManager#createWorkingSetEditWizard(org.eclipse.ui.IWorkingSet) * @since 2.1 */ @Override public IWorkingSetEditWizard createWorkingSetEditWizard( IWorkingSet workingSet) { String editPageId = workingSet.getId(); WorkingSetRegistry registry = WorkbenchPlugin.getDefault() .getWorkingSetRegistry(); IWorkingSetPage editPage = null; if (editPageId != null) { editPage = registry.getWorkingSetPage(editPageId); } // the following block kind of defeats IWorkingSet.isEditable() and it // doesn't make sense for there to be a default page in such a case. if (editPage == null) { editPage = registry.getDefaultWorkingSetPage(); if (editPage == null) { return null; } } WorkingSetEditWizard editWizard = new WorkingSetEditWizard(editPage); editWizard.setSelection(workingSet); return editWizard; } /** * @deprecated use createWorkingSetSelectionDialog(parent, true) instead */ @Deprecated @Override public IWorkingSetSelectionDialog createWorkingSetSelectionDialog( Shell parent) { return createWorkingSetSelectionDialog(parent, true); } @Override public IWorkingSetSelectionDialog createWorkingSetSelectionDialog( Shell parent, boolean multi) { return createWorkingSetSelectionDialog(parent, multi, null); } /** * {@inheritDoc} */ @Override public IWorkingSetNewWizard createWorkingSetNewWizard(String[] workingSetIds) { WorkingSetDescriptor[] descriptors= getSupportedEditableDescriptors(workingSetIds); if (descriptors.length == 0) { return null; } return new WorkingSetNewWizard(descriptors); } //---- working set delta handling ------------------------------------------------- @Override public void bundleChanged(BundleEvent event) { String symbolicName = event.getBundle().getSymbolicName(); if (symbolicName == null) return; // If the workbench isn't running anymore simply return. if (!PlatformUI.isWorkbenchRunning()) { return; } if (event.getBundle().getState() == Bundle.ACTIVE) { final WorkingSetDescriptor[] descriptors = WorkbenchPlugin.getDefault() .getWorkingSetRegistry().getUpdaterDescriptorsForNamespace( symbolicName); Job job = new WorkbenchJob( NLS .bind( WorkbenchMessages.AbstractWorkingSetManager_updatersActivating, symbolicName)) { @Override public IStatus runInUIThread(IProgressMonitor monitor) { synchronized (updaters) { for (WorkingSetDescriptor descriptor : descriptors) { List workingSets = getWorkingSetsForId(descriptor .getId()); if (workingSets.size() == 0) { continue; } final IWorkingSetUpdater updater = getUpdater(descriptor); for (Iterator iter = workingSets.iterator(); iter .hasNext();) { final IWorkingSet workingSet = (IWorkingSet) iter .next(); SafeRunner.run(new WorkingSetRunnable() { @Override public void run() throws Exception { if (!updater.contains(workingSet)) { updater.add(workingSet); } } }); } } } return Status.OK_STATUS; } }; job.setSystem(true); job.schedule(); } } private List getWorkingSetsForId(String id) { List result= new ArrayList(); for (IWorkingSet ws : workingSets) { if (id.equals(ws.getId())) { result.add(ws); } } return result; } private void addToUpdater(final IWorkingSet workingSet) { WorkingSetDescriptor descriptor= WorkbenchPlugin.getDefault() .getWorkingSetRegistry().getWorkingSetDescriptor(workingSet.getId()); if (descriptor == null || !descriptor.isUpdaterClassLoaded()) { return; } synchronized(updaters) { final IWorkingSetUpdater updater= getUpdater(descriptor); SafeRunner.run(new WorkingSetRunnable() { @Override public void run() throws Exception { if (!updater.contains(workingSet)) { updater.add(workingSet); } }}); } } private IWorkingSetUpdater getUpdater(WorkingSetDescriptor descriptor) { IWorkingSetUpdater updater = updaters.get(descriptor.getId()); if (updater == null) { updater = descriptor.createWorkingSetUpdater(); if (updater == null) { updater = NULL_UPDATER; } else { firePropertyChange(CHANGE_WORKING_SET_UPDATER_INSTALLED, null, updater); PlatformUI.getWorkbench().getExtensionTracker().registerObject( descriptor.getConfigurationElement().getDeclaringExtension(), updater, IExtensionTracker.REF_WEAK); } updaters.put(descriptor.getId(), updater); } return updater; } IWorkingSetElementAdapter getElementAdapter(WorkingSetDescriptor descriptor) { IWorkingSetElementAdapter elementAdapter = elementAdapters.get(descriptor.getId()); if (elementAdapter == null) { elementAdapter = descriptor.createWorkingSetElementAdapter(); if (elementAdapter == null) { elementAdapter = IDENTITY_ADAPTER; } else { elementAdapters.put(descriptor.getId(), elementAdapter); } } return elementAdapter; } private void removeFromUpdater(final IWorkingSet workingSet) { synchronized (updaters) { final IWorkingSetUpdater updater = updaters.get(workingSet.getId()); if (updater != null) { SafeRunner.run(new WorkingSetRunnable() { @Override public void run() throws Exception { updater.remove(workingSet); } }); } } } @Override public IWorkingSetSelectionDialog createWorkingSetSelectionDialog(Shell parent, boolean multi, String[] workingsSetIds) { return new WorkingSetSelectionDialog(parent, multi, workingsSetIds); } /** * Save the state to the state file. * * @param stateFile * @throws IOException */ public void saveState(File stateFile) throws IOException { XMLMemento memento = XMLMemento .createWriteRoot(IWorkbenchConstants.TAG_WORKING_SET_MANAGER); saveWorkingSetState(memento); saveMruList(memento); FileOutputStream stream = new FileOutputStream(stateFile); OutputStreamWriter writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8); memento.save(writer); writer.close(); } @Override public void addExtension(IExtensionTracker tracker, IExtension extension) { // nothing - this is handled lazily. These items are only created as needed by the getUpdater() and getElementAdapter() methods } @Override public void removeExtension(IExtension extension, Object[] objects) { for (Object object : objects) { if (object instanceof IWorkingSetUpdater) { removeUpdater((IWorkingSetUpdater)object); } if (object instanceof IWorkingSetElementAdapter) { removeElementAdapter((IWorkingSetElementAdapter) object); } } } /** * Remove the element adapter from the manager and dispose of it. * * @param elementAdapter * @since 3.3 */ private void removeElementAdapter( final IWorkingSetElementAdapter elementAdapter) { SafeRunner.run(new WorkingSetRunnable() { @Override public void run() throws Exception { elementAdapter.dispose(); } }); synchronized (elementAdapters) { elementAdapters.values().remove(elementAdapter); } } /** * Remove the updater from the manager and dispose of it. * * @param updater * @since 3.3 */ private void removeUpdater(final IWorkingSetUpdater updater) { SafeRunner.run(new WorkingSetRunnable() { @Override public void run() throws Exception { updater.dispose(); } }); synchronized (updaters) { updaters.values().remove(updater); } firePropertyChange(IWorkingSetManager.CHANGE_WORKING_SET_UPDATER_UNINSTALLED, updater, null); } @Override public void addToWorkingSets(final IAdaptable element, IWorkingSet[] workingSets) { // ideally this method would be in a static util class of some kind but // we dont have any such beast for working sets and making one for one // method is overkill. for (final IWorkingSet workingSet : workingSets) { SafeRunner.run(new WorkingSetRunnable() { @Override public void run() throws Exception { IAdaptable[] adaptedNewElements = workingSet .adaptElements(new IAdaptable[] { element }); if (adaptedNewElements.length == 1) { IAdaptable[] elements = workingSet.getElements(); IAdaptable[] newElements = new IAdaptable[elements.length + 1]; System.arraycopy(elements, 0, newElements, 0, elements.length); newElements[newElements.length - 1] = adaptedNewElements[0]; workingSet.setElements(newElements); } }}); } } @Override public void setRecentWorkingSetsLength(int length) { if (length < 1 || length > 99) throw new IllegalArgumentException("Invalid recent working sets length: " + length); //$NON-NLS-1$ IPreferenceStore store = PrefUtil.getAPIPreferenceStore(); store.setValue(IWorkbenchPreferenceConstants.RECENTLY_USED_WORKINGSETS_SIZE, length); // adjust length sizeRecentWorkingSets(); } private void sizeRecentWorkingSets() { int maxLength = getRecentWorkingSetsLength(); while (recentWorkingSets.size() > maxLength) { int lastPosition = recentWorkingSets.size() - 1; recentWorkingSets.remove(lastPosition); } } @Override public int getRecentWorkingSetsLength() { IPreferenceStore store = PrefUtil.getAPIPreferenceStore(); return store.getInt(IWorkbenchPreferenceConstants.RECENTLY_USED_WORKINGSETS_SIZE); } }